#include "widget.h"
#include "ui_widget.h"
#include "ui_dialog.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    this->setWindowTitle("Modbus RTU slave");

    this->setWindowIcon(QIcon(WINDOWS_ICO_PATH));

    //数据初始化
    InitData();

}

//串口热拔插事件
bool Widget::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
    int i = 0;
    MSG* msg = reinterpret_cast<MSG*>(message);
    if (msg->message == WM_DEVICECHANGE)//发生硬件热插拔事件，更新可用串口号
    {
        QString serialName = ui->serialID->currentText();
        if(msg->wParam==DBT_DEVICEARRIVAL&& serialPortStatus == 0 )
        {
            ui->serialID->clear();
            //        //通过QSerialPortInfo查找可用串口
            foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
            {   //在portBox中显示可用串口
                ui->serialID->addItem(info.portName());
            }

        }
        else if(msg->wParam==DBT_DEVICEREMOVECOMPLETE)
        {
            ui->serialID->clear();
            foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
            {   //在portBox中显示可用串口
                ui->serialID->addItem(info.portName());
                if(info.portName()== serialName){
                    i=1;
                }
            }
            if(serialPortStatus ==1&&i!=1)
            {
                serialPort->close();
                QMessageBox::information(this,"提示","串口已断开");
                serialPortStatus = 0;
            }
            qDebug()<<ui->serialID->currentText();
        }
        //if(DBT_DEVICEARRIVAL)

        return true;
    }
    return false;
}

Widget::~Widget()
{
    delete ui;
}

void Widget::InitData()
{

    clock = startTimer(CLOCK_REFRESH);
    RecordTimer = new QTimer(this);
    RecordTimer->setInterval(600000);
    RecordTimer->start();

    if(!QFile::exists("./log.txt"))
    {
        QFile file1("./log.txt");
        file1.open(QIODevice::WriteOnly);
        file1.close();
    }
    //设置白色字体
    QPalette pe;
    pe.setColor(QPalette::WindowText,Qt::white);
    ui->label->setPalette(pe);
    ui->label_2->setPalette(pe);
    ui->label_3->setPalette(pe);
    ui->label_4->setPalette(pe);
    ui->label_5->setPalette(pe);
    ui->label_6->setPalette(pe);
    ui->timeShow->setPalette(pe);
    ui->groupBox->setPalette(pe);

    pic.load(BACK_PATH);

    //写历史消息
    //    connect(ui->messageEdit,&QTextEdit::textChanged,[=](){
    //        QFile file("./log.txt");
    //        file.open(QFileDevice::Append);
    //        file.write(ui->messageEdit->toPlainText().toUtf8().data());
    //        //消息分块
    //        file.write("\n\n");
    //        qDebug()<<"write log";
    //        file.close();
    //        ui->messageEdit->clear();
    //    });
    //串口刷新
    connect(ui->pushButton,&QPushButton::clicked,[=](){
        ui->serialID->clear();
        //通过QSerialPortInfo查找可用串口
        foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
        {   //在portBox中显示可用串口
            ui->serialID->addItem(info.portName());
        }
    });

    connect(ui->slaveAddr,&QLineEdit::textEdited,[=](){
        if(ui->slaveAddr->text().toInt()<1||ui->slaveAddr->text().toInt()>127)
        {
            ui->slaveAddr->clear();
            QMessageBox::critical(this,"提示","从机地址超出范围");
        }
    });

    connect(ui->coilSearchBtn,&QPushButton::clicked,[=](){
        int coilIndex = ui->coilLineEdit->text().toInt(nullptr,10);
        //获取搜索位置的指针
        QTableWidgetItem *coilItem = ui->coilTable->item(0,coilIndex);
        ui->coilTable->setCurrentItem(coilItem);
        //滚动到指向位置
        ui->coilTable->scrollToItem(coilItem,QAbstractItemView::PositionAtCenter);
    });
    connect(ui->registerSearchBtn,&QPushButton::clicked,[=](){
        int coilIndex = (ui->registerLineEdit->text().toInt(nullptr,10));
        //获取搜索位置的指针
        QTableWidgetItem *coilItem = ui->registerTable->item(0,coilIndex);
        ui->registerTable->setCurrentItem(coilItem);
        //滚动到指向位置
        ui->registerTable->scrollToItem(coilItem,QAbstractItemView::PositionAtCenter);
    });

    serialPort = new QSerialPort;
    //serialPortStatus = new int(0);
    
    settings = new QSettings("../Modbus-RTU-slave/Data/Data.ini",QSettings::IniFormat);

    connect(ui->openSerialPortBtn,SIGNAL(clicked()),this,SLOT(OpenSerialPort()));
    connect(ui->closeSerialPortBtn,SIGNAL(clicked()),this,SLOT(CloseSerialPort()));
    connect(serialPort,&QSerialPort::readyRead,[=](){
        RTUReadMessage();
        MessageHistory();
    });


    DisplayDataToList();
    SerialPortInfo();
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(0,0,pic);
}


void Widget::closeEvent(QCloseEvent *event)
{
    QMessageBox::StandardButton result=QMessageBox::question(this, "确认", "确定要退出程序吗？",
                                                             QMessageBox::Yes|QMessageBox::No |QMessageBox::Cancel,
                                                             QMessageBox::No);

    if (result==QMessageBox::Yes)
    {
        MessageHistory();

        event->accept();
    }
    else
        event->ignore();
}

//历史消息存储
void Widget::MessageHistory()
{
    QFile file("./log.txt");
    file.open(QFileDevice::Append);
    file.write(ui->messageEdit->toPlainText().toUtf8().data());
    //消息分块
    file.write("\n\n");
    qDebug()<<"write log";
    file.close();
}

void Widget::RTUReadMessage()
{
    //获取当前接收缓冲时间
    int receiveTime = ReceivingTime();
    //启动计时器
    ReadMessageTime = startTimer(receiveTime);
    //存入缓冲区
    bufferArray.append(serialPort->readAll());
}

//设置报文接收缓冲时间
int Widget::ReceivingTime()
{
    //RTU报文最大长度
    int maxMessageLength = RTU_MESSAGE_MAX;
    //当前波特率
    int currentBaudRate = baudRate1;
    //计算读取缓冲时间
    int recTime = (8*maxMessageLength*1000/currentBaudRate);
    //返回读取缓冲时间
    return recTime;
}

void Widget::timerEvent(QTimerEvent *event)
{
    int tmp = event->timerId();

    if(tmp == clock)
    {
        ShowTime();
    }
    else if(tmp == ReadMessageTime)
    {
        killTimer(ReadMessageTime);
        //当串口为打开状态时解析，如果关闭了就不解析
        if(serialPortStatus == 1)
        {
            MessageAnalysis(bufferArray);
        }

        bufferArray.clear();
    }

}
//数组转字符串
QString Widget::ByteArrayToString(QByteArray HexByteArr,int ConvertLen, int pattern = 0)
{
    //获得目标数组大小
    int HexByteArrSize = HexByteArr.size();
    //判断长度是否合法，如果长度大于数组长度，则设为数组长度，小于0则设置为0
    if(ConvertLen > HexByteArrSize)
    {
        ConvertLen = HexByteArrSize;
    }
    else if(ConvertLen < 0)
    {
        ConvertLen = 0;
    }

    //声明目标字符串
    QString readMes = NULL;

    for(int i = 0; i < ConvertLen; i++)
    {
        readMes += QString("%1").arg((quint8)HexByteArr.at(i),2,16,QLatin1Char('0'));
        //判断转换的模式
        if(pattern == 1)
        {
            readMes += " ";
        }
    }

    //返回转化后的十六进制字符串
    return readMes;
}

//报文解析--
//解析请求报文
bool Widget::MessageAnalysis(QByteArray MessageArray)
{
    bool analysisResult = false;

    //1. 先判断接收到的报文长度是否合法，合法最小长度为读取请求报文，为8字节
    if(MessageArray.size() < MINIMUM_MESSAGE_LENGTH || MessageArray.size() > RTU_MESSAGE_MAX_BYTE)
    {
        //消息窗口显示信息
        QString res = ByteArrayToString(MessageArray,MessageArray.size(),1);
        PrintRequstMessage(res);
        ui->messageEdit->append("报文长度不合法！");
        return false;
    }

    //分析出公共数据项
    //初始化请求报文基础信息结构体
    MessageBasicInformation MessBasicInfo;
    //将请求报文基础信息存入结构体
    //  :报文长度、目的地址、功能码、起始地址、数量项、报文的CRC校验码
    StructInitialize(&MessBasicInfo, MessageArray);

    //初始化报文字符串
    QString readMessage;
    //将十六进制数据转化成字符串，加空格
    readMessage = ByteArrayToString(MessageArray, MessBasicInfo.Size, 1);
    //显示
    PrintRequstMessage(readMessage);

    //从机地址判断
    if(MessBasicInfo.SlaveAddress != ui->slaveAddr->text().toInt())
    {
        ui->messageEdit->append("从机地址不正确！");
        return false;
    }

    //    //功能码判断
    //    if((MessBasicInfo.FuncCode!=1)&&(MessBasicInfo.FuncCode!=3)&&(MessBasicInfo.FuncCode!=15)&&(MessBasicInfo.FuncCode!=16))
    //    {
    //        ui->messageEdit->append("该请求报文功能码不正确！");
    //        AbnorResMess(MessBasicInfo.SlaveAddress, MessBasicInfo.FuncCode, 1, 1);
    //        return false;
    //    }

    qDebug()<< MessBasicInfo.FuncCode;

    //1. 判断CRC校验码是否正确，如果错误则返回
    bool judgeCRC = CRCTest(&MessBasicInfo, MessageArray);
    if(judgeCRC == false)
    {
        return false;
    }

    //2. 判断从站地址和本机地址是否匹配
    if(SlaveAddr != MessBasicInfo.SlaveAddress)
    {
        return false;
    }

    //到此报文长度合法、传输无误、目标地址是本机
    //判断功能码合法性
    if(MessBasicInfo.FuncCode != 0x01
            && MessBasicInfo.FuncCode != 0x03
            && MessBasicInfo.FuncCode != 0x0f
            && MessBasicInfo.FuncCode != 0x10)
    {
        //异常响应报文发送和显示
        AbnorResMess(MessBasicInfo.SlaveAddress, MessBasicInfo.FuncCode, 1, 1);
        return false;
    }

    //到此功能码是合法的
    //4. 判断合法功能码的类型，并进行后续操作
    switch(MessBasicInfo.FuncCode)
    {
    case 0x01:
    {
        //功能码0X01和0X03请求报文解析函数
        analysisResult = AnalysisMessageRead(&MessBasicInfo, 41);
        break;
    }

    case 0x03:
    {
        //功能码0X01和0X03请求报文解析函数
        analysisResult = AnalysisMessageRead(&MessBasicInfo, 43);
        break;
    }

    case 0x0f:
    {
        //功能码0X0F请求报文解析函数
        analysisResult = AnalysisMessage0X0F(&MessBasicInfo, MessageArray);
        break;
    }

    case 0x10:
    {
        //功能码0X10请求报文解析函数
        analysisResult = AnalysisMessage0X10(&MessBasicInfo, MessageArray);
        break;
    }
    }

    return analysisResult;
}

//CRC测试
bool Widget::CRCTest(MessageBasicInformation *structPt, QByteArray messageArr)
{
    QString checkStr,checkStrCrc;
    //取出crc校验码之前的数据，计算其crc校验码，与得到的crc校验码进行对比
    checkStr = ByteArrayToString(messageArr, structPt->Size - 2, 0);
    //计算出数据的当前CRC校验码
    checkStrCrc = CRCCheck(checkStr);

    //比较两个CRC校验码是否相同
    if(checkStrCrc != structPt->CrcCheck)
    {
        //消息窗口显示信息
        ui->messageEdit->append("该报文CRC校验出错！");
        return false;
    }
    else
    {
        return true;
    }
}
//CRC校验码
QString Widget::CRCCheck(QString &checkStr)
{
    QString checkStrCrc; //计算出的CRC
    quint8 buffer;  //定义中间变量
    quint16 wcrc16 = 0xFFFF; //预置CRC校验码，全为1
    QByteArray data  = QByteArray::fromHex(checkStr.toLocal8Bit());

    //循环计算每个数据
    for (int i = 0; i < data.size(); i++ )
    {
        buffer = data.at(i);
        wcrc16 = wcrc16 ^ buffer;
        for(int j = 0; j < 8; j++)
        {
            //判断右移出的是不是1，如果是1则与多项式进行异或
            if(wcrc16 & 0x0001)
            {
                //右移一位
                wcrc16 = wcrc16 >> 1;
                //异或
                wcrc16 = wcrc16 ^ 0xA001;
            }
            else
            {
                //如果不是1直接移出
                wcrc16 = wcrc16 >> 1;
            }
        }
    }

    //拼凑成4个16进制字符，空位补0
    checkStrCrc = QString("%1").arg(wcrc16, 4, 16, QLatin1Char('0'));

    //交换高低位
    QString leftBuffer = checkStrCrc.mid(0,2);
    QString rightBuffer = checkStrCrc.mid(2,2);
    //拼接
    checkStrCrc = rightBuffer + leftBuffer;

    return checkStrCrc;
}


//时间
void Widget::ShowTime()
{
    QDateTime time = QDateTime::currentDateTime();
    QString str = time.toString(CLOCK_FORMAT);
    ui->timeShow->setText(str);
}

//时间信息打印
void Widget::timeInformationPrint()
{
    ui->messageEdit->append("");
    ui->messageEdit->append("[" + QDateTime::currentDateTime().toString("yyyy-M-dd hh:mm:ss") + "]");
}

//0X01/0X03请求报文解析函数
//   错误标识:
//         41.该请求报文的请求无法满足！所请求的线圈数量大于该起始地址可以读取的线圈数量！
//         43.该请求报文的请求无法满足！所请求的寄存器数量大于该起始地址可以读取的线圈数量！
bool Widget::AnalysisMessageRead(MessageBasicInformation *structPt, int errorMark)
{
    //判断请求帧长度，01功能码请求报文为固定长度8
    if(structPt->Size != REQUEST_MESSAGE_LENGTH_0X01_0X03)
    {
        //消息窗口显示信息
        ui->messageEdit->append("该请求报文长度不正确！");
        return false;
    }

    //判断请求报文的起始地址是否合法
    if(structPt->BeginAddress < ADDRESS_MIN || structPt->BeginAddress > ADDRESS_MAX)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 2, 2);
        return false;
    }
    if(structPt->FuncCode == 1)
    {
        //判断请求报文的线圈数量项是否合法
        if(structPt->Number < READ_COIL_MINNUM || structPt->Number > READ_COIL_MAXNUM)
        {
            //异常响应报文发送和显示
            AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 3, 3);
            return false;
        }
    }
    else
    {
        //判断请求报文的寄存器数量项是否合法
        if(structPt->Number < READ_REGISTER_MINNUM || structPt->Number > READ_REGISTER_MAXNUM)
        {
            //异常响应报文发送和显示
            AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 3, 3);
            return false;
        }
    }


    //判断该起始地址能否读取请求数量的线圈
    if((structPt->BeginAddress + structPt->Number) > (ADDRESS_MAX + 1))
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 2, errorMark);
        return false;
    }

    //正常响应报文发送并显示
    NorResMessSend(structPt);
    return true;
}


void Widget::OpenSerialPort()
{
    //定义波特率并初始化
    QSerialPort::BaudRate bauRate = QSerialPort::Baud115200;      //波特率
    QSerialPort::DataBits dataBits = QSerialPort::Data8;          //数据位
    QSerialPort::StopBits stopBits = QSerialPort::OneStop;        //停止位
    QSerialPort::Parity checkBits = QSerialPort::EvenParity;      //校验位

    //波特率设置
    if(ui->baudID->currentText()=="4800")
    {
        bauRate = QSerialPort::Baud4800;
        baudRate1 = 4800;
    }
    else if(ui->baudID->currentText()=="9600")
    {
        bauRate = QSerialPort::Baud9600;
        baudRate1 = 9600;
    }
    else if(ui->baudID->currentText()=="115200")
    {
        bauRate = QSerialPort::Baud115200;
        baudRate1 = 115200;
    }


    //设置数据位
    if(ui->dataID->currentText()=="5")
    {
        dataBits = QSerialPort::Data5;
    }
    else if(ui->dataID->currentText()=="6")
    {
        dataBits = QSerialPort::Data6;
    }
    else if(ui->dataID->currentText()=="7")
    {
        dataBits = QSerialPort::Data7;
    }
    else if(ui->dataID->currentText()=="8")
    {
        dataBits = QSerialPort::Data8;
    }

    //设置停止位
    if(ui->stopID->currentText()=="1")
    {
        stopBits = QSerialPort::OneStop;
    }
    else if(ui->stopID->currentText()=="1.5")
    {
        stopBits = QSerialPort::OneAndHalfStop;
    }
    else if(ui->stopID->currentText()=="2")
    {
        stopBits = QSerialPort::TwoStop;
    }

    //设置校验位
    if(ui->checkID->currentText()=="none")
    {
        checkBits = QSerialPort::NoParity;
    }
    else if(ui->checkID->currentText()=="奇校验")
    {
        checkBits = QSerialPort::OddParity;
    }
    else if(ui->checkID->currentText()=="偶校验")
    {
        checkBits = QSerialPort::EvenParity;
    }

    //填充串口对象的属性值
    serialPort->setPortName(ui->serialID->currentText());
    serialPort->setBaudRate(bauRate);
    serialPort->setDataBits(dataBits);
    serialPort->setStopBits(stopBits);
    serialPort->setParity(checkBits);

    if(serialPort->open(QIODevice::ReadWrite)==true)
    {
        QMessageBox::information(this,"提示","打开成功");
        serialPortStatus = 1;
        //打开成功后设置从站地址
        //        SlaveAddr = ui->slaveAddr->text().toInt();
        if(ui->slaveAddr->text() == nullptr)
        {
            SlaveAddr = 1;
            ui->slaveAddr->setText("1");
        }
        else
        {
            SlaveAddr = ui->slaveAddr->text().toInt();
        }
    }
    else
    {
        QMessageBox::critical(this,"提示","失败");
    }

}

void Widget::SerialPortInfo()
{
    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()){
        serialNamePort<<info.portName();
    }
    ui->serialID->addItems(serialNamePort);

}
//关闭串口
void Widget::CloseSerialPort()
{
    serialPort->close();
    serialPortStatus = 0;
    QMessageBox::information(this,"提示","关闭成功");
}


//字节反转函数
void byteReverse(QString &coils)
{
    // 定义临时字符变量
    QChar temp;

    for(int i=0; i < 4; i++)
    {
        temp = coils[i];        // 将第i个元素存入临时字符变量
        coils[i] = coils[8-i-1];  // 将第i个字符元素和第n-i-1个元素对调
        coils[8-i-1] = temp;    // 将临时字符变量的值赋给第n-i-1个元素
    }
}

//读取数据到表格
void Widget::DisplayDataToList()
{
    QString readData;
    ui->coilTable->setRowCount(2);
    ui->coilTable->setColumnCount(MAX_NUMBER+1);
    ui->registerTable->setRowCount(2);
    ui->registerTable->setColumnCount(MAX_NUMBER+1);

    for(int i = 0;i<MAX_NUMBER+1 ;i++)
    {
        QString addr =  "0x" + QString("%1").arg(i,4,16,QLatin1Char('0'));

        ui->coilTable->setItem(0,i,new QTableWidgetItem(QString(addr)));
        readData = settings->value("Section" + QString::number(i+1) + "/coil").toString();
        readData=="1"?readData="ON":readData="OFF";
        ui->coilTable->setItem(1,i,new QTableWidgetItem(QString(readData)));

        ui->registerTable->setItem(0,i, new QTableWidgetItem(QString(addr)));
        readData = settings->value("Section" + QString::number(i+1) + "/regi").toString();
        ui->registerTable->setItem(1,i,new QTableWidgetItem(QString(readData)));


        ui->coilTable->item(0,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->coilTable->item(1,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->registerTable->item(0,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->registerTable->item(1,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);

    }
    ui->coilTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    //ui->registerTable->item(1,i)->setBackground(const QBrush &brush);
}

//打印请求报文
void Widget::PrintRequstMessage(QString Message)
{
    //时间
    timeInformationPrint();
    //消息窗口显示信息
    ui->messageEdit->append("收到来自串口 "
                            + ui->serialID->currentText()
                            + " 的报文："
                            + Message);
}
//初始化结构体
void Widget::StructInitialize(MessageBasicInformation *structPt, QByteArray messageArr)
{
    //报文长度
    structPt->Size = messageArr.size();
    //目的地址
    structPt->SlaveAddress = (quint8)messageArr.at(0);
    //功能码
    structPt->FuncCode = (quint8)messageArr.at(1);
    //起始地址
    structPt->BeginAddress = BondTwoUint8ToUint16((quint8)messageArr.at(2),(quint8)messageArr.at(3));
    //数量项
    structPt->Number = BondTwoUint8ToUint16((quint8)messageArr.at(4),(quint8)messageArr.at(5));
    //报文的CRC校验码
    structPt->CrcCheck = QString("%1").arg((quint8)messageArr.at(structPt->Size - 2),2,16,QLatin1Char('0'))
            + QString("%1").arg((quint8)messageArr.at(structPt->Size - 1),2,16,QLatin1Char('0'));
}

//连接两个quint8数据为一个quint16数据
quint16 Widget::BondTwoUint8ToUint16(quint8 preNum, quint8 afterNum)
{
    quint16 bondNum = (preNum << 8) | afterNum;
    return bondNum;
}

//异常响应管理
void Widget::AbnorResMess(quint8 localAddr, quint8 funcCode, int abnorCode, int errorMark)
{
    //初始化响应报文数组和字符串和错误信息字符串
    QByteArray responseMessageArr;
    QString responseMessageStr;
    QString abnorResMessPrompt;

    //消息窗口显示错误类型
    switch (errorMark)
    {
    case 1:
    {
        abnorResMessPrompt = "非法功能码！";
        break;
    }
    case 2:
    {
        abnorResMessPrompt = "该请求报文的起始地址不合法！";
        break;
    }
    case 3:
    {
        abnorResMessPrompt = "该请求报文的数量项不合法！";
        break;
    }
    case 41:
    {
        abnorResMessPrompt = "该请求报文的请求无法满足！所请求的线圈数量大于该起始地址可以读取的线圈数量！";
        break;
    }
    case 42:
    {
        abnorResMessPrompt = "该请求报文的请求无法满足！所请求的线圈数量大于该起始地址可以写入的线圈数量！";
        break;
    }
    case 43:
    {
        abnorResMessPrompt = "该请求报文的请求无法满足！所请求的寄存器数量大于该起始地址可以读取的线圈数量！";
        break;
    }
    case 44:
    {
        abnorResMessPrompt = "该请求报文的请求无法满足！所请求的寄存器数量大于该起始地址可以写入的寄存器数量！";
        break;
    }
    case 5:
    {
        abnorResMessPrompt = "该请求报文的字节字段不合法！";
        ui->messageEdit->append(abnorResMessPrompt);
        return;
        break;
    }
    }

    //显示异常
    ui->messageEdit->append(abnorResMessPrompt);

    //得到异常响应报文数组
    responseMessageArr = AbnormalResponseMessage(localAddr, funcCode, abnorCode);
    //生成显示报文字符串，加空格
    responseMessageStr = ByteArrayToString(responseMessageArr, ABNORMAL_RESPONSE_LENGTH, 1);

    //发送响应报文
    serialPort->write(responseMessageArr);

    //显示异常响应报文
    ui->messageEdit->append("已向主机发送异常响应报文：" + responseMessageStr);
}



//正常响应报文线圈数据获取函数
QByteArray Widget::NorResseMessCoil(MessageBasicInformation *structPt)
{
    //声明读取的数据字符串
    QString getDataString;
    //声明响应报文字节数项
    quint8 responseMessageByteNum;
    //求响应报文字节数
    responseMessageByteNum = quint8((structPt->Number + 7) / 8);

    //从数据表中读取需要数量的线圈数据,形成二进制形式字符串
    for(int i = structPt->BeginAddress; i < (structPt->BeginAddress + structPt->Number); i++)
    {
        QString buffer = ui->coilTable->item(1,i)->text();
        if(buffer == "ON")
        {
            getDataString += "1";
        }
        else
        {
            getDataString += "0";
        }
    }

    //二进制字符串补0
    for(int i = 1; i <= (8*responseMessageByteNum - structPt->Number); i++)
    {
        getDataString += "0";
    }

    //声明线圈数据数组
    QByteArray coilsDataArr;
    coilsDataArr.resize(responseMessageByteNum);
    //将二进制字符串按字节填入响应报文数组
    for(int i = 0; i < responseMessageByteNum; i++)
    {
        //对8位1字节进行反转处理
        QString buffer = getDataString.mid((8 * i),8);
        //字节反转
        byteReverse(buffer);
        //存入响应报文数组
        coilsDataArr[i] = buffer.toInt(NULL,2);
    }

    return coilsDataArr;
}

//异常响应报文数组构建
QByteArray Widget::AbnormalResponseMessage(quint8 localAddr, quint8 funcCode,int abnorCode)
{
    QString checkStringCrc;
    QString abnormalResponseStr;
    abnormalResponseStr.resize(5);
    QByteArray abnormalResponseArr;
    quint8 abnormalCode;

    switch (abnorCode)
    {
    case 1:
    {
        abnormalCode = 0x01;
        break;
    }
    case 2:
    {
        abnormalCode = 0x02;
        break;
    }
    case 3:
    {
        abnormalCode = 0x03;
        break;
    }
    }

    abnormalResponseStr = QString("%1").arg((quint8)localAddr,2,16,QLatin1Char('0'))
            + QString("%1").arg((quint8)(funcCode + 0x80),2,16,QLatin1Char('0'))
            + QString("%1").arg((quint8)abnormalCode,2,16,QLatin1Char('0'));

    //求出前3位数据的CRC校验码
    checkStringCrc = CRCCheck(abnormalResponseStr);

    //拼接上校验码
    abnormalResponseStr += checkStringCrc;

    //转换成QByteArray
    for(int i = 0; i < 5; i++)
    {
        abnormalResponseArr[i] = abnormalResponseStr.mid(2*i,2).toInt(NULL,16);
    }

    return abnormalResponseArr;
}
//正常响应报文发送
//   参数1：自定义结构体对象
//   参数2：对象消息字节数组
void Widget::NorResMessSend(MessageBasicInformation *structPt)
{
    //初始化响应报文数组和字符串
    QByteArray responseMessageArr;
    QString responseMessageStr;

    //生成正常响应报文
    switch (structPt->FuncCode)
    {
    case 0x01:
    {
        //0x01正常响应报文构建函数
        QByteArray byteArray;
        byteArray = NorResseMessCoil(structPt);
        responseMessageArr = NorResseMessRead(structPt, byteArray);
        break;
    }
    case 0x03:
    {
        //0x03正常响应报文构建函数
        QByteArray byteArray;
        byteArray = NorResseMessRegister(structPt);
        responseMessageArr = NorResseMessRead(structPt, byteArray);
        break;
    }
    default:
    {
        //0x0f和0x10正常响应报文构建函数
        responseMessageArr = NorResseMessWrite(structPt);
        break;
    }
    }

    //由响应报文数组得到响应报文字符串
    responseMessageStr = ByteArrayToString(responseMessageArr, responseMessageArr.size(), 1);

    //发送响应报文
    serialPort->write(responseMessageArr);
    //消息窗口显示信息
    timeInformationPrint();
    ui->messageEdit->append("请求报文解析成功！");
    ui->messageEdit->append("已向主机发送响应报文：" + responseMessageStr);
}

//5. 0x03正常响应报文寄存器数据获取函数
QByteArray Widget::NorResseMessRegister(MessageBasicInformation *structPt)
{
    //声明读取的数据字符串
    QString getDataString;
    //声明响应报文字节数项
    quint8 responseMessageByteNum;

    //求响应报文字节数项，为请求报文数量项的两倍，一个寄存器两个字节
    responseMessageByteNum = (structPt->Number)*2;

    //声明线圈数据数组
    QByteArray registersDataArr;
    registersDataArr.resize(responseMessageByteNum);

    //从数据表中读取需要数量的寄存器数据,形成二进制形式字符串
    for(int i = 0; i < structPt->Number; i++)
    {
        //取出10进制字符串
        getDataString = ui->registerTable->item(1,(i + structPt->BeginAddress))->text();
        //转化为2进制字符串，不足补0，凑足16位
        getDataString  = QString("%1").arg((quint16)getDataString.toInt(NULL,10),16,2,QLatin1Char('0'));
        //前8位为一个字节
        registersDataArr[2*i] = getDataString.mid(0,8).toInt(NULL,2);
        //后8位为一个字节
        registersDataArr[2*i + 1] = getDataString.mid(8,8).toInt(NULL,2);
    }

    return registersDataArr;
}

//0x01和0x03正常响应报文构建函数
//   参数1：请求报文基础信息结构体指针
//   参数2：数据字节数组
QByteArray Widget::NorResseMessRead(MessageBasicInformation *structPt, QByteArray byteArr)
{
    //1. 初始化响应报文数组和字符串
    //声明响应报文数组
    QByteArray responseMessageArr;
    //声明响应报文CRC检测字符串
    QString responseMessageCheckString;
    //声明响应报文字节数项
    quint8 responseMessageByteNum;
    //声明响应报文CRC校验码
    QString responseMessageCRCCheck;

    //求响应报文字节数
    if(structPt->FuncCode == 1)
    {
        responseMessageByteNum = (quint8)((structPt->Number + 7) / 8);
    }
    else
    {
        responseMessageByteNum = 2*structPt->Number;
    }

    //设置响应报文数组大小
    int responseMessageArrSize = responseMessageByteNum + 5;
    responseMessageArr.resize(responseMessageArrSize);
    //从站地址
    responseMessageArr[0] = structPt->SlaveAddress;
    //功能码
    responseMessageArr[1] = structPt->FuncCode;
    //字节数
    responseMessageArr[2] = responseMessageByteNum;

    //将数据字节项填入响应报文
    for(int i = 0; i < responseMessageByteNum; i++)
    {
        responseMessageArr[3 + i] = byteArr[i];
    }

    //添加CRC校验
    //先将响应现有响应报文数组转为2位16进制字符串形式
    responseMessageCheckString = ByteArrayToString(responseMessageArr, (responseMessageArrSize - 2), 0);

    //计算出响应报文的当前CRC校验码字符串
    responseMessageCRCCheck = CRCCheck(responseMessageCheckString);;

    //添加CRC校验码到响应报文数组
    responseMessageArr[responseMessageArrSize - 2] = responseMessageCRCCheck.mid(0,2).toInt(NULL,16);
    responseMessageArr[responseMessageArrSize - 1] = responseMessageCRCCheck.mid(2,2).toInt(NULL,16);

    //返回响应报文数组
    return responseMessageArr;
}

//7. 0x0f和0x10正常响应报文构建函数
//   参数：请求报文基础信息结构体指针
QByteArray Widget::NorResseMessWrite(MessageBasicInformation *structPt)
{
    //声明响应报文数组
    QByteArray responseMessageArr;
    //声明响应报文CRC检测字符串
    QString responseMessageCheckString;
    //声明响应报文CRC校验码
    QString responseMessageCRCCheck;
    //设置响应报文数组大小
    int responseMessageArrSize = WRITE_RESPONSE_LENGTH;
    responseMessageArr.resize(responseMessageArrSize);

    //将从站地址、功能码、起始地址、数量项赋值给响应报文数组
    //从站地址
    responseMessageArr[0] = structPt->SlaveAddress;
    //功能码
    responseMessageArr[1] = structPt->FuncCode;
    //起始地址
    responseMessageArr[2] = structPt->BeginAddress >> 8;
    responseMessageArr[3] = structPt->BeginAddress & 0x0ff;
    //数量项
    responseMessageArr[4] = structPt->Number >> 8;
    responseMessageArr[5] = structPt->Number & 0x0ff;

    //添加CRC校验
    //先将响应现有响应报文数组转为2位16进制字符串形式
    responseMessageCheckString = ByteArrayToString(responseMessageArr, (responseMessageArrSize - 2), 0);
    //计算出响应报文的当前CRC校验码字符串
    responseMessageCRCCheck = CRCCheck(responseMessageCheckString);

    //添加CRC校验码到响应报文数组
    responseMessageArr[6] = responseMessageCRCCheck.mid(0,2).toInt(NULL,16);
    responseMessageArr[7] = responseMessageCRCCheck.mid(2,2).toInt(NULL,16);

    //返回响应报文数组
    return responseMessageArr;
}

//功能码0X0F请求报文解析函数
//   参数1：请求报文基础信息结构体指针
//   参数2：请求报文数组
bool Widget::AnalysisMessage0X0F(MessageBasicInformation *structPt, QByteArray messageArr)
{
    //判断请求报文的起始地址是否合法
    if(structPt->BeginAddress < ADDRESS_MIN || structPt->BeginAddress > ADDRESS_MAX)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 2, 2);
        return false;
    }
    //判断请求报文的数量项是否合法
    if(structPt->Number < WRITE_COIL_MINNUM || structPt->Number > WRITE_COIL_MAXNUM)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 3, 3);
        return false;
    }
    //判断该起始地址能否写入数量项的线圈
    if((structPt->BeginAddress + structPt->Number) > (ADDRESS_MAX + 1))
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 2, 42);
        return false;
    }

    //求响应报文字节数
    quint8 MessByteNum = (quint8)((structPt->Number + 7) / 8);

    quint8 MessArrByteNum = (quint8)messageArr.at(6);
    //判断字节数字段是否正确
    if(MessByteNum != MessArrByteNum)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 3, 5);
        return false;
    }

    //判断字节字段长度是否匹配
    if(MessArrByteNum != (structPt->Size - 9))
    {
        //消息窗口显示信息
        ui->messageEdit->append("该请求报文长度不正确！");

        return false;
    }

    //如果都正确，将数据写入线圈数据表
    QString dataObtained;
    //取出要写的数据字节，并显示，数据从第7位开始
    for(int i = 0; i < MessArrByteNum; i++)
    {
        //先转化为2进制字符串
        QString str = QString::number((quint8)messageArr.at(7 + i),2);
        //再转化为2进制整形，由二进制整形转化为8位2进制字符串前面自动补0，从而保证8位
        str = QString("%1").arg((quint8)str.toInt(NULL,2),8,2,QChar('0'));
        //8bit字节倒转
        byteReverse(str);
        dataObtained += str;
    }

    //去除填充的0位，读出请求报文请求的线圈数
    dataObtained = dataObtained.left(structPt->Number);

    //正常响应报文发送并显示
    NorResMessSend(structPt);

    ui->messageEdit->append("成功写入数据");

    //更新线圈数据表和ini文件
    for(int i = 0; i < structPt->Number; i++)
    {
        int row = structPt->BeginAddress + i;
        QString coilData = dataObtained.mid(i,1);
        WriteCoilData(row, coilData);
        ui->messageEdit->append(coilData);

    }

    return true;
}

//功能码0X10请求报文解析函数
//   参数1：请求报文基础信息结构体指针
//   参数2：请求报文数组
bool Widget::AnalysisMessage0X10(MessageBasicInformation *structPt, QByteArray messageArr)
{
    //判断请求报文的起始地址是否合法
    if(structPt->BeginAddress < ADDRESS_MIN || structPt->BeginAddress > ADDRESS_MAX)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 2, 2);
        return false;
    }

    //判断请求报文的数量项是否合法
    if(structPt->Number < WRITE_REGISTER_MINNUM || structPt->Number > WRITE_REGISTER_MAXNUM)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 3, 3);
        return false;
    }

    //判断该起始地址能否写入数量项的寄存器
    if((structPt->BeginAddress + structPt->Number) > (ADDRESS_MAX + 1))
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 2, 44);
        return false;
    }

    quint8 MessByteNum = (structPt->Number)*2;
    quint8 MessArrByteNum = (quint8)messageArr.at(6);
    //判断字节数字段是否正确
    if(MessByteNum != MessArrByteNum)
    {
        //异常响应报文发送和显示
        AbnorResMess(structPt->SlaveAddress, structPt->FuncCode, 3, 5);
        return false;
    }

    //判断字节字段长度是否匹配
    if(MessArrByteNum != (structPt->Size - 9))
    {
        //消息窗口显示信息
        ui->messageEdit->append("该请求报文长度不正确！");
        return false;
    }

    //如果都正确，将数据写入寄存器数据表
    QString dataObtained;

    //取出要写的寄存器，并显示，数据从第7个字节开始
    int row = structPt->BeginAddress;
    for(int i = 0; i < MessArrByteNum; i += 2)
    {
        dataObtained = QString::number(BondTwoUint8ToUint16((quint8)messageArr.at(7 + i),(quint8)messageArr.at(8 + i)));
        WriteRegistData(row, dataObtained);
        row++;
    }

    //正常响应报文发送并显示
    NorResMessSend(structPt);

    return true;
}

//写入线圈数据
void Widget::WriteCoilData(int row, QString coilData)
{
    //锁住写入线圈数据信号，进行阻塞
    ui->coilTable->blockSignals(true);

    //更新ini文件数据
    settings->setValue("Section" + QString::number(row + 1) + "/coil",coilData);

    //更新线圈数据表中数据
    if(coilData == "1")
    {
        coilData = "ON";
    }
    else
    {
        coilData = "OFF";
    }
    ui->coilTable->setItem(1,row,new QTableWidgetItem(coilData));
    //设置表格内文字水平+垂直对齐
    ui->coilTable->item(1,row)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);

    //解锁写入线圈数据信号
    ui->coilTable->blockSignals(false);
}

//写入寄存器数据函数
void Widget::WriteRegistData(int row, QString registerData)
{
    //锁住写入寄存器数据信号，进行阻塞
    ui->registerTable->blockSignals(true);

    //更新ini文件数据
    settings->setValue("Section" + QString::number(row + 1) + "/regi",registerData);

    //更新线圈数据表中数据
    ui->registerTable->setItem(1,row,new QTableWidgetItem(registerData));

    //设置表格内文字水平+垂直对齐
    ui->registerTable->item(1,row)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);

    //解锁写入寄存器数据信号
    ui->registerTable->blockSignals(false);
}

void Widget::on_historyBtn_clicked()
{
    QFile file("./log.txt");

    file.open(QFileDevice::ReadOnly);

    //读取文件
    QByteArray array;
    array = file.readAll();

    dialog = new Dialog(this);
    dialog->setModal(false);

    dialog->ui->textEdit->setText(array);
    //dialog->ui->textEdit->setPlainText(in.readAll().toUtf8());

    dialog->ui->textEdit->moveCursor(QTextCursor::End);

    QApplication::restoreOverrideCursor();
    dialog->show();

}

void Widget::on_clearBtn_clicked()
{
    ui->messageEdit->clear();
}
